<script lang="ts" setup>
import type {HoverCardContentProps} from '@/components/common/shadcn';

import type {MenuItemRegistered, MenuProvider, SubMenuProps} from '../types';

import {computed, onBeforeUnmount, onMounted, reactive, ref} from 'vue';

import {useNamespace} from '@/composables';
import {JcxHoverCard} from '@/components/common/shadcn';

import {
  createSubMenuContext,
  useMenu,
  useMenuContext,
  useMenuStyle,
  useSubMenuContext,
} from '../hooks';
import CollapseTransition from './collapse-transition.vue';
import SubMenuContent from './sub-menu-content.vue';

interface Props extends SubMenuProps {
  isSubMenuMore?: boolean;
}

defineOptions({name: 'SubMenu'});

const props = withDefaults(defineProps<Props>(), {
  disabled: false,
  isSubMenuMore: false,
});

const {parentMenu, parentPaths} = useMenu();
const {b, is} = useNamespace('sub-menu');
const nsMenu = useNamespace('menu');
const rootMenu = useMenuContext();
const subMenu = useSubMenuContext();
const subMenuStyle = useMenuStyle(subMenu);

const mouseInChild = ref(false);

const items = ref<MenuProvider['items']>({});
const subMenus = ref<MenuProvider['subMenus']>({});
const timer = ref<null | ReturnType<typeof setTimeout>>(null);

createSubMenuContext({
  addSubMenu,
  handleMouseleave,
  level: (subMenu?.level ?? 0) + 1,
  mouseInChild,
  removeSubMenu,
});

const opened = computed(() => {
  return rootMenu?.openedMenus.includes(props.path);
});
const isTopLevelMenuSubmenu = computed(
    () => parentMenu.value?.type.name === 'Menu',
);
const mode = computed(() => rootMenu?.props.mode ?? 'vertical');
const rounded = computed(() => rootMenu?.props.rounded);
const currentLevel = computed(() => subMenu?.level ?? 0);
const isFirstLevel = computed(() => {
  return currentLevel.value === 1;
});

const contentProps = computed((): HoverCardContentProps => {
  const isHorizontal = mode.value === 'horizontal';
  const side = isHorizontal && isFirstLevel.value ? 'bottom' : 'right';
  return {
    collisionPadding: {top: 20},
    side,
    sideOffset: isHorizontal ? 5 : 10,
  };
});

const active = computed(() => {
  let isActive = false;

  Object.values(items.value).forEach((item) => {
    if (item.active) {
      isActive = true;
    }
  });

  Object.values(subMenus.value).forEach((subItem) => {
    if (subItem.active) {
      isActive = true;
    }
  });
  return isActive;
});

function addSubMenu(subMenu: MenuItemRegistered) {
  subMenus.value[subMenu.path] = subMenu;
}

function removeSubMenu(subMenu: MenuItemRegistered) {
  Reflect.deleteProperty(subMenus.value, subMenu.path);
}

/**
 * 点击submenu展开/关闭
 */
function handleClick() {
  const mode = rootMenu?.props.mode;
  if (
      // 当前菜单禁用时，不展开
      props.disabled ||
      (rootMenu?.props.collapse && mode === 'vertical') ||
      // 水平模式下不展开
      mode === 'horizontal'
  ) {
    return;
  }

  rootMenu?.handleSubMenuClick({
    active: active.value,
    parentPaths: parentPaths.value,
    path: props.path,
  });
}

function handleMouseenter(event: FocusEvent | MouseEvent, showTimeout = 300) {
  if (event.type === 'focus') {
    return;
  }

  if (
      (!rootMenu?.props.collapse && rootMenu?.props.mode === 'vertical') ||
      props.disabled
  ) {
    if (subMenu) {
      subMenu.mouseInChild.value = true;
    }
    return;
  }
  if (subMenu) {
    subMenu.mouseInChild.value = true;
  }

  timer.value && window.clearTimeout(timer.value);
  timer.value = setTimeout(() => {
    rootMenu?.openMenu(props.path, parentPaths.value);
  }, showTimeout);
  parentMenu.value?.vnode.el?.dispatchEvent(new MouseEvent('mouseenter'));
}

function handleMouseleave(deepDispatch = false) {
  if (
      !rootMenu?.props.collapse &&
      rootMenu?.props.mode === 'vertical' &&
      subMenu
  ) {
    subMenu.mouseInChild.value = false;
    return;
  }

  timer.value && window.clearTimeout(timer.value);

  if (subMenu) {
    subMenu.mouseInChild.value = false;
  }
  timer.value = setTimeout(() => {
    !mouseInChild.value && rootMenu?.closeMenu(props.path, parentPaths.value);
  }, 300);

  if (deepDispatch) {
    subMenu?.handleMouseleave?.(true);
  }
}

const menuIcon = computed(() =>
    active.value ? props.activeIcon || props.icon : props.icon,
);

const item = reactive({
  active,
  parentPaths,
  path: props.path,
});

onMounted(() => {
  subMenu?.addSubMenu?.(item);
  rootMenu?.addSubMenu?.(item);
});

onBeforeUnmount(() => {
  subMenu?.removeSubMenu?.(item);
  rootMenu?.removeSubMenu?.(item);
});
</script>
<template>
  <li
      :class="[
      b(),
      is('opened', opened),
      is('active', active),
      is('disabled', disabled),
    ]"
      @focus="handleMouseenter"
      @mouseenter="handleMouseenter"
      @mouseleave="() => handleMouseleave()"
  >
    <template v-if="rootMenu.isMenuPopup">
      <JcxHoverCard
          :content-class="[
          rootMenu.theme,
          nsMenu.e('popup-container'),
          is(rootMenu.theme, true),
          opened ? '' : 'hidden',
        ]"
          :content-props="contentProps"
          :open="true"
          :open-delay="0"
      >
        <template #trigger>
          <SubMenuContent
              :class="is('active', active)"
              :icon="menuIcon"
              :is-menu-more="isSubMenuMore"
              :is-top-level-menu-submenu="isTopLevelMenuSubmenu"
              :level="currentLevel"
              :path="path"
              @click.stop="handleClick"
          >
            <template #title>
              <slot name="title"></slot>
            </template>
          </SubMenuContent>
        </template>
        <div
            :class="[nsMenu.is(mode, true), nsMenu.e('popup')]"
            @focus="(e) => handleMouseenter(e, 100)"
            @mouseenter="(e) => handleMouseenter(e, 100)"
            @mouseleave="() => handleMouseleave(true)"
        >
          <ul
              :class="[nsMenu.b(), is('rounded', rounded)]"
              :style="subMenuStyle"
          >
            <slot></slot>
          </ul>
        </div>
      </JcxHoverCard>
    </template>

    <template v-else>
      <SubMenuContent
          :class="is('active', active)"
          :icon="menuIcon"
          :is-menu-more="isSubMenuMore"
          :is-top-level-menu-submenu="isTopLevelMenuSubmenu"
          :level="currentLevel"
          :path="path"
          @click.stop="handleClick"
      >
        <slot name="content"></slot>
        <template #title>
          <slot name="title"></slot>
        </template>
      </SubMenuContent>
      <CollapseTransition>
        <ul
            v-show="opened"
            :class="[nsMenu.b(), is('rounded', rounded)]"
            :style="subMenuStyle"
        >
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>
  </li>
</template>
